package org.opens.lambda.expression;

import com.baomidou.mybatisplus.core.toolkit.support.BiIntFunction;
import com.google.common.base.Function;
import com.google.common.base.Supplier;
import com.google.common.collect.Lists;
import org.junit.Test;
import org.opens.lambda.UserEntity;

import java.util.Comparator;
import java.util.List;
import java.util.function.Consumer;

/**
 * 功能:
 *      lambda表达式的函数推导.
 */
public class MethodReference {

    /**
     * 介绍:
     *      1. 函数推导, 其实叫做参数列表与返回值类型自动推导更合适, 就是lambda会自动根据其指向的函数式接口来判断出自己的参数
     *      是什么类型, 返回值是什么类型, 但是只有在以下这些情况中, 才能正常使用:
     *          - 静态方法可以被推导; 例如当前类中的print方法再被传统方式lambda方式调用时, 就会自动判断出参数类型和返回值类型.
     *          - 类的实例方法可以被推导, 如testMethodReference(), 可以发现, 被引用的函数并不是函数式方法, lambda会根据引用的函数来自动推导参数类型和返回值类型, 生成一个函数式
     *          接口.
     *              1. 类的函数推导出的函数式接口的第一个参数是对象实例.
     *              2. 最后一个参数类型是返回值类型;
     *              3. 第一个参数和最后一个参数之间的参数是参数类型;
     *          - 还有一种就是实例的方法可以被推导, 但是需要注意的是:
     *              1. 没有参数的方法不可以被推导, 但是可以通过类来推导;
     * 注意:
     *      1. 上面的结论可能并不是正确的, 还是要多验证; 虽然可能用处并不多.
     */
    @Test
    public void test1() {
        final String str = "zhang";
        //传统方式定义lambda
        Consumer<String> consumer = s -> System.out.println(s);
        print(consumer, str);
        //直接传入lambda表达式
        print(s -> System.out.println(s), str);
        //使用方法引用
        print(System.out::println, str);

    }

    public static <T> void print(Consumer<T> consumer, T obj) {
        consumer.accept(obj);
    }

    /**
     * 功能:
     *      这个是类的实例方法推导.
     */
    @Test
    public void testMethodReference() {
        BiIntFunction<String, Character> function = String::charAt;
        //此处就用到了函数推导, 这个在stream中用的比较多
        Character charResult = function.apply("abcde", 2);
        System.out.println(charResult);
    }

    /**
     * 功能:
     *      这个是类的实例的方法推导.
     */
    @Test
    public void testInstanceMethodReference() {
        String str = "abcde";
        Supplier<Boolean> emptyFlag = str::isEmpty;
        //str::length;//这句会报错.
        Function<String, Integer> stringIntegerFunction = String::length;
        System.out.println(emptyFlag.get());
    }

    /**
     * 功能:
     *      构造函数引用.
     */
    @Test
    public void testMethodReferenceNew() {
        //引用无参构造
        Supplier<String> supplier1 = String::new;
        //引用有参构造
        Function<String, String> supplier2 = String::new;
        String s1 = supplier1.get();
        String s2 = supplier2.apply("sdsd");
        System.out.println(s1);
        System.out.println(s2);
    }

    /**
     * 功能:
     *      1. 使用函数推导排序list.
     */
    @Test
    public void testListSort() {
        List<UserEntity> list = Lists.newArrayList();
        list.add(new UserEntity(1, "ss", 23));
        list.add(new UserEntity(2, "xx", 25));
        list.add(new UserEntity(3, "qq", 24));
        System.out.println(list);
        list.sort((one, two) -> one.getName().compareTo(two.getName()));
        //直接使用方法引用, 请看源码, 会发现Comparator.comparing这个方法时返回了一个FunctionInterface.
        list.sort(Comparator.comparing(UserEntity::getName));
        System.out.println(list);

    }
}
